﻿using System;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Text;
using System.Net.Mail;
using System.Xml;
using System.Reflection;

namespace SledgeHammer
{
    public class Wumper
    {
        private enum XmlSettingIDs
        {
            settings,
            sites,
            targetPage,
            intervalMinutes,
            logResults,
            logFile,
            sendAlerts,
            smtpServer,
            smtpUser,
            smtpPassword,
            fromEmail,
            toEmail,
            alertSubject,
            alertMessage
        }

        #region Public Properties

        private List<string> _sites;
        public List<string> Sites
        {
            get
            {
                return _sites;
            }
            set
            {
                _sites = value;
            }
        }

        public string TargetPage
        {
            get
            {
                return Properties.Settings.Default.targetPage;
            }
        }

        public bool LogResults
        {
            get
            {
                return Properties.Settings.Default.logResults;
            }
        }

        public string LogFile
        {
            get
            {
                return Properties.Settings.Default.logFile;
            }
        }

        public bool SendAlerts
        {
            get
            {
                return Properties.Settings.Default.sendAlerts;
            }
        }

        public string SmtpServer
        {
            get
            {
                return Properties.Settings.Default.smtpServer;
            }
        }

        public int SmtpPort
        {
            get
            {
                return Properties.Settings.Default.smtpPort;
            }
        }

        public string SmtpUser
        {
            get
            {
                return Properties.Settings.Default.smtpUser;
            }
        }

        public string SmtpPassword
        {
            get
            {
                return Properties.Settings.Default.smtpPassword;
            }
        }

        public string FromEmail
        {
            get
            {
                return Properties.Settings.Default.fromEmail;
            }
        }

        public string ToEmail
        {
            get
            {
                return Properties.Settings.Default.toEmail;
            }
        }

        public string AlertSubject
        {
            get
            {
                return Properties.Settings.Default.alertSubject;
            }
        }

        public string AlertMessage
        {
            get
            {
                return Properties.Settings.Default.alertMessage;
            }
        }

        public int IntervalMinutes
        {
            get
            {
                return Properties.Settings.Default.intervalMinutes;
            }
        }

        public int IntervalHours
        {
            get
            {
                //caluclate the number of hours it wants to wait and ignore any extra minutes
                int interval = Properties.Settings.Default.intervalMinutes;
                interval = interval % 60 == 0 ? 
                    (interval /60) : 
                    (interval - (interval % 60))/60;

                //the minimum wait is 1 hour
                return (interval > 0 ? interval : 1);
            }
        }

        #endregion

        #region Constructors

        public Wumper()
        {
            //load the sites from the default settings
            LoadSites();
        }

        public Wumper(bool syncSettings)
        {
            //import the settings if there are any to import
            ImportSettings();

            //load the sites from the default settings
            LoadSites();
        }

        #endregion

        #region Proerties and Settings Methods

        public void LoadSites()
        {
            /*This method forces a reload of the sites from the app.config*/

            //create an array of sites from the app.config
            _sites = new List<string>(Properties.Settings.Default.sites.Trim().Split(';'));

            //loop backwards through and remove any that are empty
            for (int i = (_sites.Count - 1); i > -1; i--)
            {
                //clear any space
                _sites[i] = _sites[i].Trim();

                //if it's empty delete it
                if (string.IsNullOrEmpty(_sites[i]))
                {
                    _sites.RemoveAt(i);
                }
            }
        }

        public void UpdateSettings(string semiColonDeliniatedSites, string targetPage, int intervalMinutes, bool logResults, string logFile, bool sendAlert,
            string smtpServer, int smtpPort, string smtpUser, string smtpPassword, string fromEmail, string toEmail, string alertSubject,
            string alertMessage)
        {
            try
            {
                //update all the settings
                Properties.Settings.Default.sites = semiColonDeliniatedSites;
                Properties.Settings.Default.targetPage = targetPage;
                Properties.Settings.Default.logResults = logResults;
                Properties.Settings.Default.logFile = logFile;
                Properties.Settings.Default.sendAlerts = sendAlert;
                Properties.Settings.Default.smtpServer = smtpServer;
                Properties.Settings.Default.smtpPort = smtpPort;
                Properties.Settings.Default.smtpUser = smtpUser;
                Properties.Settings.Default.smtpPassword = smtpPassword;
                Properties.Settings.Default.fromEmail = fromEmail;
                Properties.Settings.Default.toEmail = toEmail;
                Properties.Settings.Default.alertSubject = alertSubject;
                Properties.Settings.Default.alertMessage = alertMessage;
                Properties.Settings.Default.intervalMinutes = intervalMinutes;

                //save the new settings
                Properties.Settings.Default.Save();

                //export the new settings so they can be picked up by the AutoWumper
                ExportSettings();

                //reload the sites into the _site object
                LoadSites();
            }
            catch
            {
                //do nothing for now, just don't fail
            }
        }

        private void ExportSettings()
        {
            //XML export file
            string xmlFileName = SettingsTransferFile();

            //if we're updating the settings anyway, if the file exists, delete it
            if (File.Exists(xmlFileName))
            {
                File.Delete(xmlFileName);
            }

            //Create a new XmlDocument
            XmlDocument xmdSettings = new XmlDocument();

            #region Build XmlStructure

            //add the declaration
            XmlDeclaration xmDec = xmdSettings.CreateXmlDeclaration("1.0", null, null);
            xmdSettings.AppendChild(xmDec);

            //start with a root node of the settings
            XmlElement xmeRoot = xmdSettings.CreateElement(XmlSettingIDs.settings.ToString());
            xmdSettings.AppendChild(xmeRoot);

            //add the children
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.sites.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.targetPage.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.intervalMinutes.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.logResults.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.logFile.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.sendAlerts.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.smtpServer.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.smtpUser.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.smtpPassword.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.fromEmail.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.toEmail.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.alertSubject.ToString()));
            xmeRoot.AppendChild(xmdSettings.CreateElement(XmlSettingIDs.alertMessage.ToString()));

            #endregion

            #region Fill the XmlStructure with the settings

            xmeRoot[XmlSettingIDs.sites.ToString()].InnerText = Properties.Settings.Default.sites;
            xmeRoot[XmlSettingIDs.targetPage.ToString()].InnerText = Properties.Settings.Default.targetPage;
            xmeRoot[XmlSettingIDs.intervalMinutes.ToString()].InnerText = Properties.Settings.Default.intervalMinutes.ToString();
            xmeRoot[XmlSettingIDs.logResults.ToString()].InnerText = Properties.Settings.Default.logResults.ToString();
            xmeRoot[XmlSettingIDs.logFile.ToString()].InnerText = Properties.Settings.Default.logFile;
            xmeRoot[XmlSettingIDs.sendAlerts.ToString()].InnerText = Properties.Settings.Default.sendAlerts.ToString();
            xmeRoot[XmlSettingIDs.smtpServer.ToString()].InnerText = Properties.Settings.Default.smtpServer;
            xmeRoot[XmlSettingIDs.smtpUser.ToString()].InnerText = Properties.Settings.Default.smtpUser;
            xmeRoot[XmlSettingIDs.smtpPassword.ToString()].InnerText = Properties.Settings.Default.smtpPassword;
            xmeRoot[XmlSettingIDs.fromEmail.ToString()].InnerText = Properties.Settings.Default.fromEmail;
            xmeRoot[XmlSettingIDs.toEmail.ToString()].InnerText = Properties.Settings.Default.toEmail;
            xmeRoot[XmlSettingIDs.alertSubject.ToString()].InnerText = Properties.Settings.Default.alertSubject;
            xmeRoot[XmlSettingIDs.alertMessage.ToString()].InnerText = Properties.Settings.Default.alertMessage;

            #endregion

            //save the file
            xmdSettings.Save(xmlFileName);
        }

        private void ImportSettings()
        {
            //XML import file
            string xmlFileName = SettingsTransferFile();

            //if we're updating the settings anyway, if the file exists, delete it
            if (File.Exists(xmlFileName))
            {
                //load the settigns update file
                XmlDocument xmdSettings = new XmlDocument();
                xmdSettings.Load(xmlFileName);
                
                //get the root of the settings
                XmlElement xmeRoot = xmdSettings[XmlSettingIDs.settings.ToString()];
                
                #region Import the settings from the update file

                Properties.Settings.Default.sites = xmeRoot[XmlSettingIDs.sites.ToString()].InnerText;
                Properties.Settings.Default.targetPage = xmeRoot[XmlSettingIDs.targetPage.ToString()].InnerText;
                Properties.Settings.Default.intervalMinutes = int.Parse(xmeRoot[XmlSettingIDs.intervalMinutes.ToString()].InnerText);
                Properties.Settings.Default.logResults = bool.Parse(xmeRoot[XmlSettingIDs.logResults.ToString()].InnerText);
                Properties.Settings.Default.logFile = xmeRoot[XmlSettingIDs.logFile.ToString()].InnerText;
                Properties.Settings.Default.sendAlerts = bool.Parse(xmeRoot[XmlSettingIDs.sendAlerts.ToString()].InnerText);
                Properties.Settings.Default.smtpServer = xmeRoot[XmlSettingIDs.smtpServer.ToString()].InnerText;
                Properties.Settings.Default.smtpUser = xmeRoot[XmlSettingIDs.smtpUser.ToString()].InnerText;
                Properties.Settings.Default.smtpPassword = xmeRoot[XmlSettingIDs.smtpPassword.ToString()].InnerText;
                Properties.Settings.Default.fromEmail = xmeRoot[XmlSettingIDs.fromEmail.ToString()].InnerText;
                Properties.Settings.Default.toEmail = xmeRoot[XmlSettingIDs.toEmail.ToString()].InnerText;
                Properties.Settings.Default.alertSubject = xmeRoot[XmlSettingIDs.alertSubject.ToString()].InnerText;
                Properties.Settings.Default.alertMessage = xmeRoot[XmlSettingIDs.alertMessage.ToString()].InnerText;
                
                #endregion

                //save the settings
                Properties.Settings.Default.Save();

                //delete the settings file now that it has been imported
                File.Delete(xmlFileName);
            }
        }

        private string SettingsTransferFile()
        {
            string xmlFileName = Path.GetDirectoryName(
                     Assembly.GetAssembly(typeof(Wumper)).CodeBase) +
                     (Properties.Settings.Default.settingUpdateFile.StartsWith(@"\") ? string.Empty : @"\") +
                     Properties.Settings.Default.settingUpdateFile;

            return (xmlFileName.StartsWith(@"file:\") ? xmlFileName.Substring(6) : xmlFileName);
        }

        #endregion

        #region TestSites

        public string TestAllSites()
        {
            //log the start of the test if deisred
            StringBuilder result = new StringBuilder(LogTestStart());

            //flag to indicate a test has failed and an email must be sent
            bool testFailed = false;

            //step through the sites and test each one
            foreach (string site in _sites)
            {
                //run the test
                string testResult = TestSite(site);

                //if the test failed raise the flag
                testFailed = testFailed || (!testResult.EndsWith("success"));

                //update the result string
                result.Append(testResult + Environment.NewLine);
            }

            //log the end of the test if desired
            result.Append(LogTestStop());

            //if the flag is raised send the alert
            if (testFailed)
            {
                SendAlert(result.ToString());
            }

            return result.ToString();
        }

        public string TestSite(string url)
        {
            //start with an empty result string
            string result = string.Empty;

            //attempt to connect to the site and return success if there is no error
            using (WebClient client = new WebClient())
            {
                try
                {
                    client.DownloadData("http://" + url + "/" +
                        (string.IsNullOrEmpty(Properties.Settings.Default.targetPage) ? 
                            string.Empty : Properties.Settings.Default.targetPage));

                    //record the result in the log if desired
                    result = url + " - success";
                }
                catch (Exception ex)
                {
                    //record the result in the log if desired
                    result = url + " - " + ex.Message;
                }

                //record to the log if needed
                WriteToLog(result);

                //return the result
                return result;
            }
        }

        #endregion

        #region Write to Log

        public static void LogServiceStartStop(bool startingUp)
        {
            try
            {
                //open a new stream to the log file
                using (StreamWriter sw = new StreamWriter(Properties.Settings.Default.logFile, true))
                {
                    //write the entry to the log
                    sw.WriteLine((startingUp ?
                            "Wumping Service Started - " + DateTime.Now.ToString() :
                            "Wumping Service Stopped - " + DateTime.Now.ToString()) +
                        Environment.NewLine);
                }
            }
            catch
            {
                //do nothing for now, just don't fail
            }
        }
           
        public string LogTestStart()
        {
            //log the start of the test to the file
            string entry = "------------------------------" + Environment.NewLine +
                "Wump Started: " + DateTime.Now.ToString() + Environment.NewLine + Environment.NewLine;

            WriteToLog(entry);

            //return the entry in case we want to update the UI with it
            return entry;
        }

        public string LogTestStop()
        {
            //log the end of the test to the file
            string entry = Environment.NewLine + 
                "Wump Stopped: " + DateTime.Now.ToString() + Environment.NewLine +
                "------------------------------" + Environment.NewLine;

            WriteToLog(entry);

            //return the entry in case we want to update the UI with it
            return entry;
        }

        private void WriteToLog(string entry)
        {
            //if theresult should be logged
            if (Properties.Settings.Default.logResults)
            {
                try
                {
                    //open a new stream to the log file
                    using (StreamWriter sw = new StreamWriter(Properties.Settings.Default.logFile, true))
                    {
                        //write the entry to the log
                        sw.WriteLine(entry);
                    }
                }
                catch
                {
                    //do nothing for now, just don't fail
                }
            }
        }

        #endregion

        #region Send Alerts

        private bool SendAlert(string result)
        {
            //if it needs to be sent
            if (Properties.Settings.Default.sendAlerts)
            {
                try
                {
                    //create a new smtp client instance
                    SmtpClient client = new SmtpClient(Properties.Settings.Default.smtpServer, Properties.Settings.Default.smtpPort);
                    client.UseDefaultCredentials = false;
                    
                    //TODO: Add credentials here

                    //send the email
                    client.Send(Properties.Settings.Default.fromEmail,
                        Properties.Settings.Default.toEmail,
                        Properties.Settings.Default.alertSubject,
                        Properties.Settings.Default.alertMessage + Environment.NewLine +
                            Environment.NewLine + result);
                }
                catch (Exception ex)
                {
                    return false;
                }
            }
            //if it didn't get an exception return true
            return true;
        }

        #endregion
    }
}
